package com.fary.boot.context.config;

import com.fary.beans.BeanUtils;
import com.fary.boot.context.event.ApplicationEnvironmentPreparedEvent;
import com.fary.context.ApplicationEvent;
import com.fary.context.ApplicationListener;
import com.fary.context.event.SimpleApplicationEventMulticaster;
import com.fary.core.Ordered;
import com.fary.core.SpringException;
import com.fary.core.annotation.AnnotationAwareOrderComparator;
import com.fary.core.env.ConfigurableEnvironment;
import com.fary.util.Assert;
import com.fary.util.ClassUtils;
import com.fary.util.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 监听应用事件，并将这些应用事件广播给环境属性context.listener.classes指定的那些监听器。
 */
public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered {

    // NOTE: Similar to org.springframework.web.context.ContextLoader

    private static final String PROPERTY_NAME = "context.listener.classes";

    private int order = 0;

    private SimpleApplicationEventMulticaster multicaster;

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            // 听到ApplicationEnvironmentPreparedEvent事件时，尝试根据环境属性
            // context.listener.classes构造相应的事件监听器
            List<ApplicationListener<ApplicationEvent>> delegates = getListeners(((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
            if (delegates.isEmpty()) {
                return;
            }
            this.multicaster = new SimpleApplicationEventMulticaster();
            for (ApplicationListener<ApplicationEvent> listener : delegates) {
                this.multicaster.addApplicationListener(listener);
            }
        }
        // 如果是ApplicationEnvironmentPreparedEvent之外的其他应用事件，将该事件
        // 直接通过事件多播器广播到环境属性context.listener.classes指定的那些事件监听器上。
        if (this.multicaster != null) {
            this.multicaster.multicastEvent(event);
        }
    }

    @SuppressWarnings("unchecked")
    private List<ApplicationListener<ApplicationEvent>> getListeners(ConfigurableEnvironment environment) {
        if (environment == null) {
            return Collections.emptyList();
        }
        // 获取环境属性context.listener.classes指定的事件监听器的类名称，0个或者多个，使用逗号分隔
        String classNames = environment.getProperty(PROPERTY_NAME);
        List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<>();
        if (StringUtils.hasLength(classNames)) {
            for (String className : StringUtils.commaDelimitedListToSet(classNames)) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
                    Assert.isAssignable(ApplicationListener.class, clazz, "class [" + className + "] must implement ApplicationListener");
                    listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils.instantiateClass(clazz));
                } catch (Exception ex) {
                    throw new SpringException("Failed to load context listener class [" + className + "]", ex);
                }
            }
        }
        AnnotationAwareOrderComparator.sort(listeners);
        return listeners;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

}